#ifndef __M_SINK_H__
#define __M_SINK_H__

#include <iostream>
#include <string>
#include <cassert>
#include <memory>
#include <atomic>
#include <fstream>
#include <ctime>
#include "util.hpp"

/*
日志落地方向类
    1.标准输出
    2.指定文件
    3.滚动文件

注：是落地方向，不是落地方式。
    落地方式指的是同步落地还是异步落地
*/

namespace MyLog
{
    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;
        LogSink() {};
        //虚析构函数必须要有实现，哪怕是纯虚函数也是如此
        //因为它是派生类析构过程的一部分，子类析构函数会调用父类的析构函数
        virtual ~LogSink() = default;
        virtual void log(const char* date, size_t len) = 0;

    };

    class StdioSink : public LogSink
    {
    public:
        StdioSink() {}
        virtual ~StdioSink() override {}
        virtual void log(const char* data, size_t len) override
        {
            std::cout.write(data, len);
        }
    };

    class FileSink : public LogSink
    {
    public:
        FileSink(const std::string& filePath) 
            :_filePath(filePath)
        {
            //先判断文件路径是否存在，不存在创建路径
            if(!File::CreateDirectory(File::GetFilePath(filePath)))
            {
                std::cout << "创建文件路径失败" << std::endl;
                assert(false);
            }

            //打开文件
            _logFile.open(filePath, std::ios::app | std::ios::binary);
            if (!_logFile.is_open()) 
            {
                std::cerr << "打开/创建文件失败！" << std::endl;
                assert(false);
            }
        }

    
        virtual void log(const char* data, size_t len) override
        {
            _logFile.write(data, len);
            if (_logFile.good() == false) {
                std::cout << "日志输出文件失败！" << std::endl;
            }
        }

        virtual ~FileSink() override 
        {
            _logFile.close();
        }

    private:
        std::string _filePath;
        std::ofstream _logFile;
    };

    // class RollBySizeSink : public LogSink
    // {
    // public:
    //     RollBySizeSink(const std::string& baseName, size_t maxFSize) 
    //         :_baseName(baseName)
    //         ,_maxFSize(maxFSize)
    //     {
    //         //对文件后缀进行处理
    //         size_t pos = _baseName.find_last_of(".");
    //         _fileSuffix = _baseName.substr(pos);
    //         if(pos != std::string::npos)
    //             _baseName.erase(pos);
            
    //         //先判断文件路径是否存在，不存在创建路径
    //         if(!File::CreateDirectory(File::GetFilePath(baseName)))
    //         {
    //             std::cout << "创建文件路径失败" << std::endl;
    //             assert(false);
    //         }
    //         //打开文件
    //         _logFile.open(CreateFileName(), std::ios::app | std::ios::binary);
    //         if (!_logFile.is_open()) 
    //         {
    //             std::cerr << "打开/创建文件失败！" << std::endl;
    //             assert(false);
    //         }
    //     }


    //     virtual ~RollBySizeSink() override 
    //     {
    //         _logFile.close();
    //     }

    //     virtual void log(const char* data, size_t len) override
    //     {
    //         //先判断当前文件能否继续写
    //         if(_curFSize >= _maxFSize)
    //         {
    //             //这里一定要关闭文件，否则内存泄漏
    //             _logFile.close();
    //             _curFSize = 0;
    //             //打开新文件
    //             _logFile.open(CreateFileName(), std::ios::app | std::ios::binary);
    //             if (!_logFile.is_open()) 
    //             {
    //                 std::cerr << "打开/创建文件失败！" << std::endl;
    //                 assert(false);
    //             }
    //         }

    //         _logFile.write(data, len);
    //         _curFSize += len;
    //     }

    // private:
    //     //生成文件名
    //     std::string CreateFileName()
    //     {
    //         std::string fileName = _baseName;
    //         fileName += "-";
    //         fileName += Time::FormatTime(Time::GetTimeStamp(), "%Y%m%d%H%M%S");
    //         fileName += "-";
    //         fileName += std::to_string(_FileCount++);
    //         fileName += _fileSuffix;
    //         return fileName;
    //     }

    // private:
    //     //最终文件名：文件基础名+时间+序号
    //     //加上序号是为了防止同一时间多个文件被写满，导致会创建相同的文件

    //     std::string _baseName;  //文件路径+基础名
    //     std::string _fileSuffix;//文件后缀
    //     std::ofstream _logFile; //对象
    //     size_t _maxFSize;       //一个文件最多写多少
    //     size_t _curFSize = 0;   //当前所用空间

    //     /// @todo 这个不是线程安全的，看后期要不要处理一下
    //     size_t _FileCount = 1;  //序号
    // };


    class RollSink : public LogSink
    {
    public:
        RollSink(const std::string& baseName) 
            :_baseName(baseName)
        {
           Init();
        }
        
        virtual ~RollSink() 
        {
            _logFile.close();
        }

        void Init()
        {
            //对文件后缀进行处理
            size_t pos = _baseName.find_last_of(".");
            _fileSuffix = _baseName.substr(pos);
            if(pos != std::string::npos)
                _baseName.erase(pos);
            
            //先判断文件路径是否存在，不存在创建路径
            if(!File::CreateDirectory(File::GetFilePath(_baseName)))
            {
                std::cout << "创建文件路径失败" << std::endl;
                assert(false);
            }
            //打开文件
            _logFile.open(CreateFileName(), std::ios::app | std::ios::binary);
            if (!_logFile.is_open()) 
            {
                std::cerr << "打开/创建文件失败！" << std::endl;
                assert(false);
            }
        }

    protected:
        //生成文件名
        std::string CreateFileName()
        {
            std::string fileName = _baseName;
            fileName += "-";
            fileName += Time::FormatTime(Time::GetTimeStamp(), "%Y%m%d%H%M%S");
            fileName += "-";
            fileName += std::to_string(_FileCount++);
            fileName += _fileSuffix;
            return fileName;
        }

    protected:
        std::string _baseName;   //文件路径+基础名
        std::string _fileSuffix; //文件后缀
        std::ofstream _logFile;  //对象

        /// @todo 这个不是线程安全的，看后期要不要处理一下
        std::atomic<size_t> _FileCount{0};  //序号
    };


    class RollBySizeSink : public RollSink
    {
    public:
        RollBySizeSink(const std::string& baseName, size_t maxFSize) 
            :RollSink(baseName)
            ,_maxFSize(maxFSize)
        {}

        virtual ~RollBySizeSink() override {}

        virtual void log(const char* data, size_t len) override
        {
            //先判断当前文件能否继续写
            if(_curFSize >= _maxFSize)
            {
                //这里一定要关闭文件，否则内存泄漏
                _logFile.close();
                _curFSize = 0;
                //打开新文件
                _logFile.open(CreateFileName(), std::ios::app | std::ios::binary);
                if (!_logFile.is_open()) 
                {
                    std::cerr << "打开/创建文件失败！" << std::endl;
                    assert(false);
                }
            }
            _logFile.write(data, len);
            _curFSize += len;
        }

    private:
        //最终文件名：文件基础名+时间+序号
        //加上序号是为了防止同一时间多个文件被写满，导致会创建相同的文件
        size_t _maxFSize;       //一个文件最多写多少
        size_t _curFSize = 0;   //当前所用空间
    };

   
    class RollByTimeSink : public RollSink
    {
    public:
        enum class GapTime
        {
            GAP_SECOND = 1,
            GAP_MINUTE = 60,
            GAP_HOUR = 3600,
            GAP_DAY = 3600 * 24
        };

        RollByTimeSink(const std::string& baseName, GapTime gapTime) 
            :RollSink(baseName)
            ,_gapTime(gapTime)
        {
            _fileTime = Time::GetTimeStamp();
        }

        virtual ~RollByTimeSink() override {}

        virtual void log(const char* data, size_t len) override
        {
            time_t nowTime = Time::GetTimeStamp();
            if(_gapTime == GapTime::GAP_SECOND ? nowTime != _fileTime \
                : nowTime > _fileTime + static_cast<int>(_gapTime))
            {
                _logFile.close();
                _fileTime = nowTime;
                 //打开新文件
                _logFile.open(CreateFileName(), std::ios::app | std::ios::binary);
                if (!_logFile.is_open()) 
                {
                    std::cerr << "打开/创建文件失败！" << std::endl;
                    assert(false);
                }
            }
            _logFile.write(data, len);
        }

    private:
        //最终文件名：文件基础名+时间+序号
        GapTime _gapTime;   //间隔时间
        time_t _fileTime;   //当前文件创建时间
    };


    // 简单工厂模式
    class SinkFactory
    {
    public:
        //不同的落地方式构造函数要的参数是不同的
        //用可变参模版
        template<class SinkType, class ...Args>
        static LogSink::ptr CreateSink(Args&& ...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };

}

#endif